// CyTargetLinker RIN conversion,
//
// scripts to create RIN network files for several online databases
// can be used with the CyTargetLinker app for Cytoscape
//
// Copyright 2011-2013 Department of Bioinformatics - BiGCaT, Maastricht University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package cytargetlinker.conversion;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.bridgedb.DataSource;
import org.bridgedb.IDMapper;

import cytargetlinker.conversion.ArgsParser.AFilesAttributes;
import cytargetlinker.conversion.ArgsParser.AFilesOut;
import cytargetlinker.conversion.ArgsParser.AHelp;
import cytargetlinker.conversion.ArgsParser.GraphBuilder;
import cytargetlinker.conversion.data.GeneNode;
import cytargetlinker.conversion.data.INode;
import cytargetlinker.conversion.data.TFGeneInteraction;
import cytargetlinker.conversion.data.TFNode;
import cytargetlinker.conversion.graph.Graph;

/**
 * Import transcription factor targets from the TFe wiki.
 * @author thomas
 */
public class TFe {
	private final static Logger log = Logger.getLogger(TFe.class.getName());

	private static Args arguments;
	private interface Args extends AHelp, AFilesOut, AFilesAttributes { }
	
	private final String TFE_URL = "http://www.cisreg.ca/cgi-bin/tfe/api.pl?";

	private Graph graph;
	
	/**
	 * USAGE: java -cp conversion.jar cytargetlinker.conversion.TFe
	 * 
	 * REQUIRED ARGUMENTS:
	 * [-o] = output file path
	 * [-m] = list of bridgedb files for identifier mapping
	 * [-l] = log file path
	 * [--organism] = scientific name for the organism
	 *  
	 * OPTIONAL ARGUMENTS:
	 * [-n] = network name
	 * [-s] = source is auto, user or both (added automatically or through an expert, or both)
	 */
	public TFe() {
		nodes = new HashMap<String, INode>();
		interactions = new HashMap<String, TFGeneInteraction>();
		nodesNotFound = new ArrayList<String>();
		interactionIgnored = new ArrayList<String>();
		geneList = new HashMap<String, TFe.Gene>();
		interactionList = new ArrayList<TFe.Interaction>();
	}
	
	private IDMapper idMapper;
	
	private Map<String, INode> nodes;
	private Map<String, TFGeneInteraction> interactions;
	
	private List<String> nodesNotFound;
	private List<String> interactionIgnored;
	
	private Map<String, Gene> geneList;
	private List<Interaction> interactionList;
	
	public void startConversion() throws Exception {
		ArgsParser.convertAndWrite(null, arguments, new GraphBuilder() {
			public Graph buildGraph(File in) throws Exception {
				return importTFe(null);
			}
		});
	}
	
	
	public Graph importTFe(String species) throws MalformedURLException, IOException {
		if(getLogFile() != null) {
			Utils.setUpLogger(log, getLogFile(), false);
			idMapper = Utils.initIDMapper(arguments, false);
			if(idMapper == null) {
				log.severe("no bridgedb file specified");
			} else {
				String type;
				if(!arguments.isSource()) {
					type = "both";
					createGraph(species, type);
				} else {
					if(arguments.getSource().equals("user") || arguments.getSource().equals("auto")) {
						createGraph(species, arguments.getSource());
					} else {
						System.out.println("Please specify a correct TFe source type [user or auto]\t" + arguments.getSource());
					}
				}
			}
		} else {
			System.out.println("Please specify log file!");
		}

		return graph;
	}
	
	private void createGraph(String species, String type) throws MalformedURLException, IOException {
		graph = new Graph();
		graph.setTitle("TFe 2012-12-05");
		graph.setAttribute(CommonAttributes.DATABASE.getName(), "TFe 2012-12-05");
		graph.setAttribute(CommonAttributes.SOURCE_FILE.getName(), TFE_URL);
		graph.setAttribute(CommonAttributes.TYPE.getName(), "TF targets");
		graph.setAttribute(CommonAttributes.IDENTIFIERS.getName(), "identifiers");
		graph.setAttribute(CommonAttributes.TARGET_DATASOURCE.getName(), "Entrez Gene");
		graph.setAttribute(CommonAttributes.SOURCE_DATASOURCE.getName(), "Entrez Gene");
		graph.setAttribute(CommonAttributes.SOURCE_TYPE.getName(), "gene");
		graph.setAttribute(CommonAttributes.TARGET_TYPE.getName(), "gene");
		
		//First get all TF ids
		String[] tfIds = readURL(TFE_URL + "code=all-tfids").split("\n");
		for(String id : tfIds) {
			//log.info("Processing " + id);
			if(species != null) {
				String tfSpecies = readURL(TFE_URL + "tfid=" + id + "&code=species").trim();
				log.info(tfSpecies);
				if(!species.equals(tfSpecies)) continue;
			}
	
			//log.info("Querying info for " + id);
			String tfId = readURL(TFE_URL + "tfid=" + id + "&code=entrez-gene-id").trim();
			
			//Get targets
			String[] lines = readURL(TFE_URL + "tfid=" + id + "&code=targets").split("\n");
			if(lines.length > 0) {
				for(String l : lines) {
					if("".equals(l)) continue;
					String[] cols = l.split("\t");
					for(int i = 0; i < cols.length; i++) cols[i] = cols[i].trim();
					
					String targetId = cols[0];

					if(type.equals("both") || cols[5].equals(type)) {
						Gene source;
						Gene target;
						if(geneList.containsKey(tfId)) {
							if(geneList.get(tfId).type.equals("gene")) {
								geneList.get(tfId).type = "transcriptionFactor";
							}
							source = geneList.get(tfId);
						} else {
							Gene gene = new Gene();
							gene.id = tfId;
							gene.type = "transcriptionFactor";
							gene.name = cols[1];
							geneList.put(tfId, gene);
							source = gene;
						}
						
						if(!geneList.containsKey(targetId)) {
							Gene gene = new Gene();
							gene.id = targetId;
							gene.type = "gene";
							gene.name = cols[1];
							geneList.put(targetId, gene);
							target = gene;
						} else {
							target = geneList.get(targetId);
						}
						
						Interaction in = new Interaction();
						in.source = source;
						in.target = target;
						interactionList.add(in);
					} 
				}
			}
		}
		
		for(Interaction interaction : interactionList) {
			String source = createNode(interaction.source.id, interaction.source.name, interaction.source.type);
			String target = createNode(interaction.target.id, interaction.target.name, interaction.target.type);
			
			if(source != null && target != null) {
				if(!interactions.containsKey(source + "_" + target)) {
					
					TFGeneInteraction i = new TFGeneInteraction(nodes.get(source), nodes.get(target), "", "", "");
					interactions.put(source + "_" + target, i);
				}
			} else {
				if(!interactionIgnored.contains(interaction.source.id + " -> " + interaction.target.id)) {
					interactionIgnored.add(interaction.source.id + " -> " + interaction.target.id);
				}
			}
		}
		
		int count = 0;
		for(String str : interactions.keySet()) {
			TFGeneInteraction i = interactions.get(str);
			i.createEdge(graph, "TFe (2012-12-05)", count);
			count++;
		}

		int genes = 0;
		int tfs = 0;
		for(String str : nodes.keySet()) {
			if(nodes.get(str) instanceof GeneNode) {
				genes++;
			} else {
				tfs++;
			}
		}
		
		log.info(interactions.size() + " interactions have been found.\n" + genes + " gene nodes.\n" + tfs + " miRNA nodes.\n");
		log.info(interactionIgnored.size() + " interactions were ignores because " + nodesNotFound.size() + " nodes could not be mapped\n");
		
		cleanUp();
	}

	private String createNode(String id, String name, String type) {
		if(type.equals("transcriptionFactor")) {
			TFNode node = TFNode.createGeneNode(id, name, idMapper, DataSource.getBySystemCode("L"), "");
			if(node != null) {
				if(!nodes.containsKey(node.getId())) {
					nodes.put(node.getId(), node);
				}
				return node.getId();
			} else {
				if(!nodesNotFound.contains(id)) {
					nodesNotFound.add(id);
				}
				return null;
			}
		} else {
			GeneNode node = GeneNode.createGeneNode(id, name, idMapper, DataSource.getBySystemCode("L"));
			if(node != null) {
				if(!nodes.containsKey(node.getId())) {
					nodes.put(node.getId(), node);
				}
				return node.getId();
			} else {
				if(!nodesNotFound.contains(id)) {
					nodesNotFound.add(id);
				}
				return null;
			}
		}
	}
	
	private class Gene {
		String id;
		String name;
		String type;
	}
	
	private class Interaction {
		Gene source;
		Gene target;
	}
	
	private String readURL(String url) throws MalformedURLException, IOException {
		return IOUtils.toString(new URL(url).openStream());
	}
	
	public static void main(String[] args) {
		try {
			arguments = ArgsParser.parse(args, Args.class);
			TFe converter = new TFe();
			converter.startConversion();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private File getLogFile() {
		if(arguments.isLogFile()) {
			return arguments.getLogFile();
		} else {
			return null;
		}
	}
	
	private void cleanUp() {
		nodes.clear();
		interactions.clear();
		nodesNotFound.clear();
		interactionIgnored.clear();
	}
}
